#include "reginfo.h"

#include "iot_debug.h"
#include "util_tools.h"
#include "json_util.h"

#include <assert.h>
#include <sstream>
#include <logger.h>

RegFloat::RegFloat(const std::string &name, DXType raw_Type, float k, float b, float diff)
 :  RegBase (name),
    raw_type_(raw_Type),
    latest_data_(0.0f),
    transfer_data_(0.0f),
    k_(k),
    b_(b),
    diff_(diff/100.0f)
{

}

std::string RegFloat::toString() const
{
    switch (raw_type_)
    {
    case TYPE_FLOAT:
    case TYPE_FLOAT_abcd:
    case TYPE_FLOAT_dcba:
    case TYPE_FLOAT_badc:
    case TYPE_FLOAT_cdab:
        return FloatToString(latest_data_);
    default:
        return FloatToString(k_, latest_data_);
    }
}

bool RegFloat::changed(bool force)
{
    if (reg_status_ == INIT)
        return false;
    if (transfer_reg_status_ == VALID ||
        force)
    {
        if (!force)
        {
            if (fabsf(transfer_data_) >= 0.001f)
            {
                float rate = fabsf((latest_data_ - transfer_data_) / transfer_data_);
                if (diff_ != 0 && rate >= diff_)
                {
                    transfer_data_ =latest_data_;
                    return true;
                }
                else if (diff_ == 0 && transfer_data_ != latest_data_)
                {
                    transfer_data_ =latest_data_;
                    return true;
                }
            }
            else
            {
                if (diff_ != 0 && fabsf(latest_data_) >= diff_)
                {
                    transfer_data_ = latest_data_;
                    return true;
                }
                else if (diff_ == 0 && transfer_data_ != latest_data_)
                {
                    transfer_data_ = latest_data_;
                    return true;
                }
            }
            return false;
        }
        else
        {
            transfer_data_ = latest_data_;
            return true;
        }
    }
    else if (transfer_reg_status_ != reg_status_)
    {
        transfer_reg_status_ = reg_status_;
        if (transfer_reg_status_ == VALID)
            transfer_data_ = latest_data_;
        return true;
    }

    return false;
}

uint8_t RegFloat::setRawData(const void *buf)
{
    const uint8_t *ptr = reinterpret_cast<const uint8_t*>(buf);
    uint8_t len = 0;
    switch (raw_type_) {
    case TYPE_INT8:
        this->latest_data_ = static_cast<int8_t>(ptr[0]);
        len=1;
        break;
    case TYPE_UINT8:
        this->latest_data_ = static_cast<uint8_t>(ptr[0]);
        len=1;
        break;
    case TYPE_INT16:
        this->latest_data_ = static_cast<int16_t>(ptr[0]<<8|ptr[1]);
        len=2;
        break;
    case TYPE_UINT16:
        this->latest_data_ = static_cast<uint16_t>(ptr[0]<<8|ptr[1]);
        len=2;
        break;
    case TYPE_INT32:
        this->latest_data_ = static_cast<int32_t>(GET_INT32_FROM_INT8(ptr, 0));
        len=4;
        break;
    case TYPE_UINT32:
        this->latest_data_ = static_cast<uint32_t>(GET_INT32_FROM_INT8(ptr, 0));
        len=4;
        break;
    case TYPE_INT32_L:
        {
            uint16_t dl = static_cast<uint16_t>(ptr[0]<<8|ptr[1]);
            uint16_t dh = static_cast<uint16_t>(ptr[2]<<8|ptr[3]);
            this->latest_data_ = static_cast<int32_t>(dh<<16|dl);
        }
        len=4;
        break;
    case TYPE_UINT32_L:
        {
            uint16_t dl = static_cast<uint16_t>(ptr[0]<<8|ptr[1]);
            uint16_t dh = static_cast<uint16_t>(ptr[2]<<8|ptr[3]);
            this->latest_data_ = static_cast<uint32_t>(dh<<16|dl);
        }
        len=4;
        break;
    case TYPE_FLOAT:
        this->latest_data_ = modbus_get_float(reinterpret_cast<const uint16_t *>(ptr));
        len=4;
        break;
    case TYPE_FLOAT_abcd:
        this->latest_data_ = modbus_get_float_abcd(reinterpret_cast<const uint16_t *>(ptr));
        len=4;
        break;
    case TYPE_FLOAT_dcba:
        this->latest_data_ = modbus_get_float_dcba(reinterpret_cast<const uint16_t *>(ptr));
        len=4;
        break;
    case TYPE_FLOAT_badc:
        this->latest_data_ = modbus_get_float_badc(reinterpret_cast<const uint16_t *>(ptr));
        len=4;
        break;
    case TYPE_FLOAT_cdab:
        this->latest_data_ = modbus_get_float_cdab(reinterpret_cast<const uint16_t *>(ptr));
        len=4;
        break;
    case TYPE_INT64:
        {
            const int16_t *d = reinterpret_cast<const int16_t *>(ptr);
            this->latest_data_ = static_cast<int64_t>(GET_INT64_FROM_INT16(d, 0));
        }
        len=8;
        break;
    case TYPE_UINT64:
        {
            const int16_t *d = reinterpret_cast<const int16_t *>(ptr);
            this->latest_data_ = static_cast<uint64_t>(GET_INT64_FROM_INT16(d, 0));
        }
        len=8;
        break;
    case TYPE_INT64_L:
        {
            uint32_t dl = static_cast<uint32_t>(ptr[2]<<24|ptr[3]<<16|ptr[0]<<8|ptr[1]);
            ptr += 4;
            uint32_t dh = static_cast<uint32_t>(ptr[2]<<24|ptr[3]<<16|ptr[0]<<8|ptr[1]);
            int64_t d = dh;
            d = d<<32 | dl;
            this->latest_data_ = d;
        }
        len=8;
        break;
    case TYPE_UINT64_L:
        {
            uint32_t dl = static_cast<uint32_t>(ptr[2]<<24|ptr[3]<<16|ptr[0]<<8|ptr[1]);
            ptr += 4;
            uint32_t dh = static_cast<uint32_t>(ptr[2]<<24|ptr[3]<<16|ptr[0]<<8|ptr[1]);
            uint64_t d = dh;
            d = d<<32 | dl;
            this->latest_data_ = d;
        }
        len=8;
        break;
    default:
        log_info("found exceprton: type(%d)\n", raw_type_);
        assert(false);
        break;
    }
    latest_data_ = latest_data_ * k_ + b_;
    reg_status_ = VALID;
    return len;
}

void RegFloat::setRawData(const char *str)
{
    if (str) {
        sscanf(str, "%f", &latest_data_);
        latest_data_ = latest_data_ * k_ + b_;
        reg_status_ = VALID;
    }
}

void RegFloat::setRealData(const char *str)
{
    if (str) {
        sscanf(str, "%f", &latest_data_);
        reg_status_ = VALID;
    }
}

float RegFloat::toRawDataFloat() const
{
    return (latest_data_-b_)/k_;
}


RegInt::RegInt(const std::string &name, DXType raw_Type, int diff) :
    RegBase (name),
    raw_type_(raw_Type),
    latest_data_(0),
    transfer_data_(INT_MIN),
    diff_(diff)
{

}

std::string RegInt::toString() const
{
    return i32toa(latest_data_);
}

bool RegInt::changed(bool force)
{
    if (reg_status_ == INIT)
        return false;
    if (transfer_reg_status_ == VALID ||
        force)
    {
        if (!force)
        {
            if (diff_)
            {
                if (transfer_data_ != 0)
                {
                    int rate = 0;
                    if (latest_data_ > transfer_data_)
                        rate = (latest_data_ - transfer_data_) * 100 / transfer_data_;
                    else if (latest_data_ < transfer_data_)
                        rate = (transfer_data_ - latest_data_) * 100 / transfer_data_;
                    if ( rate > diff_)
                    {
                        transfer_data_ =latest_data_;
                        return true;
                    }
                }
                else
                {
                    if (latest_data_*100 > diff_)
                    {
                        transfer_data_ = latest_data_;
                        return true;
                    }
                }
            }
            else
            {
                if (transfer_data_ != latest_data_)
                {
                    transfer_data_ = latest_data_;
                    return true;
                }
            }
        }
        else
        {
            transfer_data_ = latest_data_;
            return true;
        }
    }
    else if (transfer_reg_status_ != reg_status_)
    {
        transfer_reg_status_ = reg_status_;
        if (transfer_reg_status_ == VALID)
            transfer_data_ = latest_data_;
        return true;
    }

    return false;
}

uint8_t RegInt::setRawData(const void *buf)
{
    const uint8_t *ptr = reinterpret_cast<const uint8_t*>(buf);
    uint8_t len = 0;
    switch (raw_type_) {
    case TYPE_INT8:
        this->latest_data_ = static_cast<int8_t>(ptr[0]);
        len=1;
        break;
    case TYPE_INT16:
        this->latest_data_ = static_cast<int16_t>(ptr[0]<<8|ptr[1]);
        len=2;
        break;
    case TYPE_INT32:
        this->latest_data_ = GET_INT32_FROM_INT8(ptr, 0);
        len=4;
        break;
    case TYPE_INT32_L:
        {
            uint16_t dl = static_cast<uint16_t>(ptr[0]<<8|ptr[1]);
            uint16_t dh = static_cast<uint16_t>(ptr[2]<<8|ptr[3]);
            this->latest_data_ =  static_cast<int32_t>(dh<<16|dl);
        }
        len=4;
        break;
    default:
        log_info("found exceprton: type(%d)\n", raw_type_);
        assert(false);
        break;
    }
    reg_status_ = VALID;
    return len;
}

void RegInt::setRawData(const char *str)
{
    if (str) {
        sscanf(str, "%d", &latest_data_);
        reg_status_ = VALID;
    }
}


RegUint::RegUint(const std::string &name, DXType raw_Type, uint32_t diff) :
    RegBase (name),
    raw_type_(raw_Type),
    latest_data_(0),
    transfer_data_(UINT32_MAX),
    diff_(diff)
{

}

std::string RegUint::toString() const
{
    return u32toa(latest_data_);
}

bool RegUint::changed(bool force)
{
    if (reg_status_ == INIT)
        return false;
    if (transfer_reg_status_ == VALID ||
        force)
    {
        if (!force)
        {
            if (diff_)
            {
                if (transfer_data_ != 0)
                {
                    uint32_t rate = 0;
                    if (latest_data_ > transfer_data_)
                        rate = (latest_data_ - transfer_data_) * 100 / transfer_data_;
                    else if (latest_data_ < transfer_data_)
                        rate = (transfer_data_ - latest_data_) * 100 / transfer_data_;
                    if ( rate > diff_)
                    {
                        transfer_data_ =latest_data_;
                        return true;
                    }
                }
                else
                {
                    if (latest_data_*100 > diff_)
                    {
                        transfer_data_ = latest_data_;
                        return true;
                    }
                }
            }
            else
            {
                if (transfer_data_ != latest_data_)
                {
                    transfer_data_ = latest_data_;
                    return true;
                }
            }
        }
        else
        {
            transfer_data_ = latest_data_;
            return true;
        }
    }
    else if (transfer_reg_status_ != reg_status_)
    {
        transfer_reg_status_ = reg_status_;
        if (transfer_reg_status_ == VALID)
            transfer_data_ = latest_data_;
        return true;
    }

    return false;
}

uint8_t RegUint::setRawData(const void *buf)
{
    const uint8_t *ptr = reinterpret_cast<const uint8_t*>(buf);
    uint8_t len = 0;
    switch (raw_type_) {
    case TYPE_UINT8:
        this->latest_data_ = static_cast<uint8_t>(ptr[0]);
        len=1;
        break;
    case TYPE_UINT16:
        this->latest_data_ = static_cast<uint16_t>(ptr[0]<<8|ptr[1]);
        len=2;
        break;
    case TYPE_UINT32:
        this->latest_data_ = static_cast<uint32_t>(GET_INT32_FROM_INT8(ptr, 0));
        len=4;
        break;
    case TYPE_UINT32_L:
        {
            uint16_t dl = static_cast<uint16_t>(ptr[0]<<8|ptr[1]);
            uint16_t dh = static_cast<uint16_t>(ptr[2]<<8|ptr[3]);
            this->latest_data_ =  static_cast<uint32_t>(dh<<16|dl);
        }
        len=4;
        break;
    default:
        log_info("found exceprton: type(%d)\n", raw_type_);
        assert(false);
        break;
    }
    reg_status_ = VALID;
    return len;
}

void RegUint::setRawData(const char *str)
{
    if (str) {
        sscanf(str, "%u", &latest_data_);
        reg_status_ = VALID;
    }
}


RegBit::RegBit(const std::string &name, bool reverse) :
    RegBase (name),
    latest_status_(0),
    transfer_status_(0),
    reverse_(reverse)
{

}

std::string RegBit::toString() const
{
    return i32toa(latest_status_);
}

bool RegBit::changed(bool force)
{
    if (reg_status_ == INIT)
        return false;
    if (transfer_reg_status_ == VALID ||
        force)
    {
        if ((transfer_status_ != latest_status_) || (force))
        {
            transfer_status_ = latest_status_;
            return true;
        }
        return false;
    }
    else if (transfer_reg_status_ != reg_status_)
    {
        transfer_reg_status_ = reg_status_;
        if (transfer_reg_status_ == VALID)
            transfer_status_ = latest_status_;
        return true;
    }

    return force;
}

uint8_t RegBit::setRawData(const void */*buf*/)
{
    return 0;
}

void RegBit::setRawData(bool val)
{
    reg_status_ = VALID;
    latest_status_ = reverse_ ? !val : val;
}

void RegBit::setRawData(int val)
{
    reg_status_ = VALID;
    if (reverse_)
        latest_status_ = !val;
    else
        latest_status_ = val ? true : false;
}

void RegBit::setRawData(float val)
{
    setRawData(static_cast<int>(val));
}

void RegBit::setRawData(const char *str)
{
    if (str) {
        int val;
        sscanf(str, "%d", &val);
        setRawData(val);
        reg_status_ = VALID;
    }
}


RegString::RegString(const std::string &name, const std::string &data) :
    RegBase (name),
    latest_data_(data),
    transfer_data_(data)
{

}

bool RegString::changed(bool force)
{
    if (reg_status_ == INIT)
        return false;
    if (transfer_reg_status_ == VALID ||
        force)
    {
        if ((latest_data_.compare(transfer_data_) != 0) || (force))
        {
            transfer_data_ = latest_data_;
            return true;
        }
        return false;
    }
    else if (transfer_reg_status_ != reg_status_)
    {
        transfer_reg_status_ = reg_status_;
        if (transfer_reg_status_ == VALID)
            transfer_data_ = latest_data_;
        return true;
    }
    return force;
}

uint8_t RegString::setRawData(const void */*buf*/)
{
    return 1;
}

void RegString::setRawData(int val)
{
    std::ostringstream os;
    os << val;
    latest_data_ = os.str();
    reg_status_ = VALID;
}

void RegString::setRawData(float val)
{
    std::ostringstream os;
    os << val;
    latest_data_ = os.str();
    reg_status_ = VALID;
}

void RegString::setRawData(const char *str)
{
    latest_data_ = str;
    reg_status_ = VALID;
}


int parseRegs(cJSON *root, RegInfos &regs)
{
    int ret = 0;

    cJSON *js = cJSON_GetObjectItem(root, "Regs");
    if (js && js->type == cJSON_Array) {
        int item = 0;
        do {
            cJSON* arrItem = cJSON_GetArrayItem(js, item++);
            if (!arrItem)
                break;

            DXType type = TYPE_BIT1;
            std::string val_type;
            if (cJSON_GetString(arrItem, "Type", val_type)) {
                type = StringToDxType(val_type.c_str());
                if (type == TYPE_UNKNOWN) {
                    ret = MODE_TYPE_ERROR;
                    goto error;
                }
            }
            cJSON * js_name = cJSON_GetObjectItem(arrItem, "RegName");
            std::vector<std::string> reg_names = parseStringObject(js_name);
            if (reg_names.size() == 0) {
                ret = MODE_REGNAME_ERROR;
                goto error;
            }

            float k=1.0f, b=0.0f, diff = 1.0f;
            if (!cJSON_GetFloat(arrItem, "Rate", k)) {
                cJSON_GetFloat(arrItem, "k", k);
            }
            cJSON_GetFloat(arrItem, "b", b);
            cJSON_GetFloat(arrItem, "diff", diff);
            diff = fabs(diff);

            switch (type) {
            case TYPE_BIT1:
                {
                    bool reverse = false;
                    cJSON_GetFloat(arrItem, "Reverse", reverse);
                    for (std::vector<std::string>::iterator it=reg_names.begin();
                         it!=reg_names.end(); it++)
                    {
                        regs.push_back(new RegBit(*it, reverse));
                    }
                }
                break;
            case TYPE_INT8:
            case TYPE_INT16:
            case TYPE_INT32:
            case TYPE_INT32_L:
                if (fabsf(k-1.0f)>=0.001f || fabsf(b)>=0.001f) {
                    for (std::vector<std::string>::iterator it=reg_names.begin();
                         it!=reg_names.end(); it++)
                    {
                        regs.push_back(new RegFloat(*it, type, k, b, diff));
                    }
                }
                else {
                    int32_t dt = static_cast<int32_t>(diff);
                    for (std::vector<std::string>::iterator it=reg_names.begin();
                         it!=reg_names.end(); it++)
                    {
                        regs.push_back(new RegInt(*it, type, dt));
                    }
                }
                break;
            case TYPE_UINT8:
            case TYPE_UINT16:
            case TYPE_UINT32:
            case TYPE_UINT32_L:
                if (fabsf(k-1.0f)>=0.001f || fabsf(b)>=0.001f) {
                    for (std::vector<std::string>::iterator it=reg_names.begin();
                         it!=reg_names.end(); it++)
                    {
                        regs.push_back(new RegFloat(*it, type, k, b, diff));
                    }
                }
                else {
                    uint32_t dt = static_cast<uint32_t>(diff);
                    for (std::vector<std::string>::iterator it=reg_names.begin();
                         it!=reg_names.end(); it++)
                    {
                        regs.push_back(new RegUint(*it, type, dt));
                    }
                }
                break;
            case TYPE_FLOAT:
            case TYPE_FLOAT_abcd:
            case TYPE_FLOAT_badc:
            case TYPE_FLOAT_cdab:
            case TYPE_FLOAT_dcba:
                for (std::vector<std::string>::iterator it=reg_names.begin();
                     it!=reg_names.end(); it++)
                {
                    regs.push_back(new RegFloat(*it, type, k, b, diff));
                }
                break;
            case TYPE_INT64:
            case TYPE_INT64_L:
            case TYPE_UINT64:
            case TYPE_UINT64_L:
                for (std::vector<std::string>::iterator it=reg_names.begin();
                     it!=reg_names.end(); it++)
                {
                    regs.push_back(new RegFloat(*it, type, k, b, diff));
                }
                break;
            case TYPE_STR:
            case TYPE_STRING:
                for (std::vector<std::string>::iterator it=reg_names.begin();
                     it!=reg_names.end(); it++)
                {
                    regs.push_back(new RegString(*it));
                }
                break;
            default:
                break;
            }
        }while(true);
    }
    else {
        cJSON *js = cJSON_GetObjectItem(root, "RegNames");
        std::vector<std::string> reg_names = parseStringObject(js);
        if (reg_names.size() == 0)
        {
            ret = MODE_REGNAME_ERROR;
            goto error;
        }

        for (std::vector<std::string>::iterator it=reg_names.begin(); it!=reg_names.end(); it++) {
            regs.push_back(new RegBit(*it));
        }
    }

    cJSON_GetInt(root, "pollingCycle", regs.polling_cycle);
    cJSON_GetString(root, "Shell", regs.shell);

    return true;
error:
    log_info("parse failed\n");
    return ret;
}

void dumpRegData(const RegInfos &regs)
{
    for (RegInfos::const_iterator inf=regs.begin(); inf!=regs.end(); inf++) {
        RegBase *reg = *inf;
        printf("%s: %s  ", reg->name().c_str(), reg->toString().c_str());
    }
}

void dumpRegData(const RegBase* reg)
{
    if (reg)
    {
        printf("%s: %s  ", reg->name().c_str(), reg->toString().c_str());
    }
}
